package cn.me.implement.threadpool.v3_0;

import java.util.Set;

/**
 * <h3>线程池中的线程执行的任务</h3>
 * 提供了退出循环的方法：让 {@link #getTask()} 方法返回 {@code  null}
 * <p>
 * 这里用了 <strong>模板方法模式</strong>：
 * <ul>
 *     <li>{@link #run()} 和 {@link #start()} 不能被子类重写</li>
 *     <li>{@link #getTask()} 和 {@link #onWorkerExit()} 作为抽象方法强制子类重写，并在 {@link #run()} 方法中使用</li>
 * </ul>
 */
public abstract class Worker implements Runnable {

    /**
     * 线程执行的初始任务
     */
    private Runnable initialTask;

    /**
     * 对 <strong>真正运行的线程</strong> 的引用，用于调用其 {@link Thread#start()} 方法启动线程
     */
    private final Thread actuallyRunningThread;

    public Worker(Runnable initialTask, Set<Worker> threadPool) {
        this.initialTask = initialTask;
        this.actuallyRunningThread = new Thread(this);
        threadPool.add(this);
    }

    @Override
    public final void run() {
        initialTask.run();
        initialTask = null; // help GC
        try {
            while (true) {
                Runnable t = getTask();
                // 检查是否获取到任务了，如果没有则退出循环，停止运行
                if (t == null) {
                    break;
                }
                t.run();
            }
        } finally {
            onWorkerExit();
        }
    }

    /**
     * 启动内部保存的线程
     */
    public final void start() {
        actuallyRunningThread.start();
    }

    /**
     * 获取任务，当返回 @{code null} 时，这个 {@link Worker} 对象就退出循环
     * <p>
     * 使用模板方法模式，交给子类实现
     *
     * @return 获取到的任务
     */
    protected abstract Runnable getTask();

    /**
     * 当 {@link Worker} 准备退出时执行的回调函数
     * <p>
     * 一般用于将 {@link Worker} 对象从线程池中移除
     * <p>
     * 使用模板方法模式，交给子类实现
     */
    protected abstract void onWorkerExit();
}
